#include "stdafx.h"
#include "PlayerContext.h"

#include "2DUtils.h"
#include "OnWorldGui.h"
#include "Camera.h"
#include "Node.h"

#include "Army.h"
#include "Unit.h"

#include "Ray.h"

#include "GlobalAppData.h"

#include "Globals.h"

using namespace input;

static mmath::Ray getCameraRay(const mscene::Camera & camera, const Point & clickPoint)
{
	int scrWid = mcore::GlobalAppData::Instance.ScreenWidth;
	int scrHei = mcore::GlobalAppData::Instance.ScreenHeight;
	XMVECTOR rayPoint = XMVector3Unproject(
		XMVectorSet((float)clickPoint.X, (float)clickPoint.Y, 0, 0),
		0, 0, (float)scrWid, (float)scrHei, 0, 1,
		XMLoadFloat4x4(&camera.proj()),
		XMLoadFloat4x4(&camera.view()),
		XMMatrixIdentity()
		);
	XMVECTOR camPos = XMLoadFloat3(&camera.position());
	return mmath::Ray(camPos, rayPoint - camPos);
}

Unit* PlayerContext::checkForObjectSelection(const Point & clickPos)
{
	mmath::Ray ray = getCameraRay(g_gameScene->getCamera(), clickPos);

	float closestDist = std::numeric_limits<float>::infinity();
	Unit * closestUnit = NULL;

	for (uint armyIdx = 0; armyIdx < g_gameScene->mNumArmies; armyIdx++)
	{
		std::vector<Unit> & units = g_gameScene->mArmies[armyIdx]->mUnits;
		for(uint u = 0; u < units.size(); u++)
		{
			mmath::BoundingSphere unitBSphere = units[u].getBoundingSphere();
			XMVECTOR spherePos = XMLoadFloat3(&unitBSphere.Center);
			float dist = ray.intersectSphere(spherePos, unitBSphere.Radius);

			if(dist < closestDist && dist >= 0)
			{
				closestDist = dist;
				closestUnit = &units[u];
			}
		}
	}

	return closestUnit;
}

void PlayerContext::checkForMultipleObjectSelection(const Point & p1, const Point & p2, std::vector<Unit*> & outUnits)
{
	uint scrWid = mcore::GlobalAppData::Instance.ScreenWidth;
	uint scrHei = mcore::GlobalAppData::Instance.ScreenHeight;

	uint minX = min(p1.X, p2.X);
	uint maxX = max(p1.X, p2.X);
	uint minY = min(p1.Y, p2.Y);
	uint maxY = max(p1.Y, p2.Y);

	for (uint armyIdx = 0; armyIdx < g_gameScene->mNumArmies; armyIdx++)
	{
		std::vector<Unit> & units = g_gameScene->mArmies[armyIdx]->mUnits;
		for(uint u = 0; u < units.size(); u++)
		{
			XMVECTOR screenPos = XMVector3Transform(XMLoadFloat3(&units[u].Position), g_gameScene->getCamera().getViewProjAligned());
			float posX = XMVectorGetX(screenPos) / XMVectorGetW(screenPos);
			float posY = XMVectorGetY(screenPos) / XMVectorGetW(screenPos);

			uint x = (uint)(( posX * 0.5f + 0.5f) * scrWid);
			uint y = (uint)((-posY * 0.5f + 0.5f) * scrHei);

			if(x >= minX && x <= maxX && y >= minY && y <= maxY)
			{
				outUnits.push_back(&units[u]);
			}
		}
	}
}

void PlayerContext::moveCommand(const Point & clickPos)
{
	for(uint i = 0; i < SelectedUnits.size(); i++)
	{
		mscene::Node * plane = g_gameScene->getNode(0);
		mmath::Ray ray = getCameraRay(g_gameScene->getCamera(), clickPos);
		float inters = plane->rayIntersection(ray);

		if(inters > 0 && inters != std::numeric_limits<float>::infinity())
		{
			XMVECTOR onTerrainPoint = XMLoadFloat3(&ray.Origin);
			onTerrainPoint += XMLoadFloat3(&ray.Direction) * inters;

			XMFLOAT3 destPoint;
			XMStoreFloat3(&destPoint, onTerrainPoint);
			SelectedUnits[i]->setCurrentDestination(destPoint);
		}


	}
}

void PlayerContext::processPlayerInput(const input::Event & event)
{
	static int mPrevMousePosX = 0, mPrevMousePosY = 0;
	static bool mCameraStrafing = false;
	static bool mCameraRotating = false;

	if(event.Type == Event::MOUSE)
	{
		MouseEvent mouseEv = event.MouseEv;

		switch(mouseEv.Type)
		{
		case MouseEvent::L_DOWN:
			DrawSelectionRectStart = mouseEv.Pos;
			//DrawSelectionRectEnd = mouseEv.Pos;
			mHoldingSelectButton = true;
			DrawingSelectionRect = false;
			break;

		case MouseEvent::L_UP:
			
			if(DrawingSelectionRect)
			{
				mTmpSelectedUnits.clear();
				checkForMultipleObjectSelection(DrawSelectionRectStart, mouseEv.Pos, mTmpSelectedUnits);
				if(mTmpSelectedUnits.size() > 0)
					SelectedUnits = mTmpSelectedUnits;
			}
			else
			{
				Unit * selected = checkForObjectSelection(mouseEv.Pos);
				if(selected != NULL)
				{
					setSelectedUnit(*selected);
				}
			}

			DrawingSelectionRect = false;
			mHoldingSelectButton = false;
			break;

		case MouseEvent::MOVE:
			{
				static bool firstTime = true;
				int mousePosX = mouseEv.Pos.X;
				int mousePosY = mouseEv.Pos.Y;
				if(firstTime)
				{
					firstTime = false;
				}
				else
				{
					int diffY = mPrevMousePosY - mousePosY;
					int diffX = mPrevMousePosX - mousePosX;
					if(diffY != 0 || diffX != 0)
					{
						if(mCameraStrafing)
						{
							g_gameScene->getCamera().strafe(diffX * 0.6f);
							g_gameScene->getCamera().moveVertically(-diffY * 0.6f);
						}
						else if(mCameraRotating)
						{
							g_gameScene->getCamera().pitch(float(-diffY) * 0.003f);
							g_gameScene->getCamera().rotateY(float(-diffX) * 0.003f);
						}
						//mScene->mouseMove(diffX, diffY);
					}

				}
				mPrevMousePosX = mousePosX;
				mPrevMousePosY = mousePosY;

				if(mHoldingSelectButton && DrawSelectionRectStart != mouseEv.Pos)
				{
					DrawSelectionRectEnd = mouseEv.Pos;
					DrawingSelectionRect = true;
				}
				else
					DrawingSelectionRect = false;
			}
			break;

		case MouseEvent::M_DOWN:
			mCameraStrafing = true;
			break;

		case MouseEvent::M_UP:
			mCameraStrafing = false;
			break;

		case MouseEvent::R_DOWN:
			mCameraRotating = true;
			moveCommand(mouseEv.Pos);
			break;

		case MouseEvent::R_UP:
			mCameraRotating = false;
			break;

		case MouseEvent::WHEEL:
			g_gameScene->getCamera().walk(mouseEv.WheelAmount * 0.4f);
			break;
		}

	}
}


void PlayerContext::setSelectedUnit( Unit & unit )
{
	SelectedUnits.clear();
	addSelectedUnit(unit);
}

void PlayerContext::addSelectedUnit(Unit & unit)
{
	SelectedUnits.push_back(&unit);
}